//
//  ViewController.m
//  RunTime
//
//  Created by 宜必鑫科技 on 2017/11/7.
//  Copyright © 2017年 宜必鑫科技. All rights reserved.
//  方法调用的本质是发送消息 runtime机制

#import "ViewController.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import "UIKeyboardController.h"
//#import "NSMutableArray+LogAddObject.h"
#import "UIView+Exchange.h"
#import "NSObject+Extension.h"
#import "Person.h"

@interface ViewController ()
- (IBAction)but:(id)sender;
@property (nonatomic, strong) UIView *myView;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    _myView = [[UIView alloc]init];
    
#pragma mark -  1.获得类里面的方法
//    [self getoObjectMethodWith:[UITextField class]];
    
#pragma mark -  2.获得类里面的所有属性
//    [self getClassAllAttributeWithClass:[NSObject class]];
    
#pragma mark -  3.能获得某个类的所有成员变量
//    [self getClassAllIvarWithClass:[UITextField class]];

#pragma mark -  4.交换方法实现
//    NSMutableArray *array = [[NSMutableArray alloc]init];
//    [array addObject:@"------------------++++++++++++++-------------------------"];
//    [array addObject:@"---------------------++++++++++++++----------------------------------"];
    
    UIButton * btn = [UIButton new];
    btn.backgroundColor = [UIColor blackColor];
    [self.view addSubview:btn];
    btn.frame = CGRectMake(0, 30, 200, 40);
    [btn setTitle:@"点击" forState:UIControlStateNormal];
    
    UIView * viw = [[UIView alloc] initWithFrame:CGRectMake(0, 100, 100, 100)];
    viw.backgroundColor = [UIColor blueColor];
    [self.view addSubview:viw];

#pragma mark -  5.能动态添加一个成员变量
#pragma mark -  6.能动态添加一个属性
        _myView.books = [NSArray array];
#pragma mark -  7.能动态添加一个方法  也就是方法的懒加载  编译的时候是没有这个方法的  调用的时候才会有这个方法
    Person *person = [[Person alloc]init];
    person.name = @"张三";
    class_addMethod([person class], @selector(viewControllereat:), (IMP)viewControllereat, "V@:");
    [person performSelector:@selector(eat)];
    NSArray *array = @[@"参数1",@"参数2",@"参数3"];
    [person performSelector:@selector(eat1:) withObject:array];
    
#pragma mark -  8. 比较基础的一个动态特性是通过String来生成Classes和Selectors。
//    Class stringclass = NSClassFromString(@"NSString");
//    SEL name_f = NSSelectorFromString(@"");
//    [self parseObjectDynamic:@"123"];
#pragma mark -  9. 修改一个对象的父类
    object_setClass(_myView, [UIButton class]);
    NSLog(@"_myView  的父类是 %@",[NSString stringWithUTF8String:class_getName([_myView class])]);
    
#pragma mark -  10.动态创建一个类和方法
    [self createMethod];
}

void viewControllereat(id self,SEL sel,Person *person)
{
    NSLog(@"eat  eat  eat  %@ %@",person.name,NSStringFromSelector(sel));
}
- (void)viewControllereat:(Person *)per{}




/**
             isa           isa
    Objects,----> Classes ----> MetaClasses 改变isa指针就可以改变一个对象的class
    object的class保存了方法列表，还有指向父类的指针
    class的方法列表其实是一个字典，key为selectors，IMPs为value。
    一个IMP是指向方法在内存中的实现。很重要的一点是，selector和IMP之间的关系是在运行时才决定的，而不是编译时。这样我们就能玩出些花样。
    IMP通常是指向方法的指针，第一个参数是self，类型为id，第二个参数是_cmd，类型为SEL，余下的是方法的参数。
    这也是self和_cmd被定义的地方。下面演示了Method和IMP
 */
- (void)getoObjectMethodWith:(id)class
{
    const char * name_s = class_getName([class class]);
    NSLog(@"类名：%@",[NSString stringWithUTF8String:name_s]);
    
    const char * name_s1 = class_getName(class_getSuperclass([class class]));
    NSLog(@"父类名：%@",[NSString stringWithUTF8String:name_s1]);
    
    u_int count;
    Method *methodList = class_copyMethodList([class class], &count);
    for (int i = 0 ; i < count; i++)
    {
        Method temp_f = methodList[i];
        // method_getImplementation  由Method得到IMP函数指针
        IMP imp_f = method_getImplementation(temp_f);
        
        // method_getName由Method得到SEL
        SEL name_f = method_getName(temp_f);
        
        const char * name_s = sel_getName(name_f);
        // method_getNumberOfArguments  由Method得到参数个数
        int arguments = method_getNumberOfArguments(temp_f);
        // method_getTypeEncoding  由Method得到Encoding 类型
        const char * encoding = method_getTypeEncoding(temp_f);
        
        NSLog(@"方法名：%@",[NSString stringWithUTF8String:name_s]);
//        NSLog(@"参数个数：%d,编码方式：%@",arguments,[NSString stringWithUTF8String:encoding]);
    }
    free(methodList);
}

// 返回的仅仅是对象类的属性(@property申明的属性)
- (void)getClassAllAttributeWithClass:(id)class
{
    u_int count;
    objc_property_t *properties = class_copyPropertyList([class class], &count);
    for (int i = 0 ; i < count; i++)
    {
        const char* propertyName =property_getName(properties[i]);
        NSLog(@"属性名 %@",[NSString stringWithUTF8String:propertyName]);
    }
    free(properties);
}

- (void)getClassAllIvarWithClass:(id)class
{
    u_int count;
    Ivar *ivars = class_copyIvarList([class class], &count);
    // 遍历所有的成员变量
    for (int i = 0; i< count; i++) {
        // 取出i位置对应的成员变量
        //            Ivar ivar = *(ivars + i);
        Ivar ivar = ivars[i];
        // 获得成员变量的名字
        NSLog(@"成员变量 %s", ivar_getName(ivar));
    }
    free(ivars);
}

- (void)createMethod
{
    // 1、 添加一个集成NSObject的类  类名是MyClass
    // 注意：调用的c语言的方法  所以不要使用@""表示字符串 应该使用""
    Class MyClass = objc_allocateClassPair([NSObject class], "MyClass", 0);
    
    // 2、增加实例变量
    // 参数一、类名
    // 参数二、属性名称
    // 参数三、开辟字节长度
    // 参数四、对其方式
    // 参数五、参数类型 “@” 官方解释 An object (whether statically typed or typed id) （对象 静态类型或者id类型） 具体类型可参照 https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
    // return: BOOL 是否添加成功
    BOOL isSuccess = class_addIvar(MyClass, "test", sizeof(NSString *), 0, "@");
    
    // 三目运算符
    isSuccess?NSLog(@"添加变量成功"):NSLog(@"添加变量失败");
    
    // 3、增加方法
    class_addMethod(MyClass, @selector(addMethodForMyClass:), (IMP)addMethodForMyClass, "V@:");
    
    //注册这个类到runtime系统中就可以使用他了
    objc_registerClassPair(MyClass);
    
    id myobjc = [[MyClass alloc] init];
    //    NSLog(@"%@",myObjc);
    
    //    在OC中，我们对方法的调用都会被转换成内部的消息发送执行对objc_msgSend方法的调用，掌握好消息发送，可以让我们在编程中更方便灵活。
    
    // 上面的id myObjc = [[MyClass alloc] init]; 我们可以通过runtime 消息发送objc_msgSend去实现
    
    // 实现[MyClass alloc] 去开辟空间
    //    id myobjc = objc_msgSend(MyClass, @selector(alloc));
    
    //
    //    myobjc = objc_msgSend(myobjc, @selector(init));
    
    NSLog(@"%@",myobjc);
    
    
    NSString *str = @"我是test";
    
    // 通过KVC的方式给myObj对象的test属性赋值
    [myobjc setValue:str forKey:@"test"];
    
    
    // 如果不调用- (void)addMethodForMyClass:(NSString *)string 这个方法，就不会调用static void addMethodForMyClass(id self, SEL _cmd, NSString *test) 函数
    [myobjc addMethodForMyClass:@"参数"];
}

//self和_cmd是必须的，在之后可以随意添加其他参数
static void addMethodForMyClass(id self, SEL _cmd, NSString *test) {
    
    // 获取类中指定名称实例成员变量的信息
    Ivar ivar = class_getInstanceVariable([self class], "test");
    
    // 获取整个成员变量列表
    //   Ivar * class_copyIvarList ( Class cls, unsigned intint * outCount );
    // 获取类中指定名称实例成员变量的信息
    //   Ivar class_getInstanceVariable ( Class cls, const charchar *name );
    // 获取类成员变量的信息
    //   Ivar class_getClassVariable ( Class cls, const charchar *name );
    
    
    // 返回名为test的ivar变量的值
    id obj = object_getIvar(self, ivar);
    
    NSLog(@"%@",obj);
    NSLog(@"addMethodForMyClass:参数：%@",test);
    NSLog(@"ClassName：%@",NSStringFromClass([self class]));
    
}


//这个方法实际上没有被调用,但是必须实现否则不会调用下面的方法
- (void)addMethodForMyClass:(NSString *)string {
}













- (IBAction)but:(id)sender {
    UIKeyboardController *vc = [[UIKeyboardController alloc]init];
    [self presentViewController:vc animated:YES completion:nil];
}
@end
